import {
  blockTagNames,
  paraTagNames,
} from './config';

import { getContentNodesFromNodeOrFrag, getParaListFromNodeOrFrag } from './get';

// para
function findPara(node) {
  if(!node) return null;
  let res = node;
  while( res && 
    !(res.nodeType === Node.ELEMENT_NODE && (paraTagNames.includes(res.tagName))
      || blockTagNames.includes(res.tagName))) {
    res = res.parentNode;
  }
  return res;
}

function findNextPara(node, root) {
  let para = findPara(node);
  let paraList = getParaListFromNodeOrFrag(root);
  let paraArr = Array.from(paraList);
  let paraIdx = paraArr.indexOf(para);
  if(paraIdx === -1) return undefined;
  if(paraIdx < paraArr.length - 1) {
    return paraArr[paraIdx + 1];
  } else {
    return null;
  }
}

function findPreviousPara(node, root) {
  let para = findPara(node);
  let paraList = getParaListFromNodeOrFrag(root);
  let paraArr = Array.from(paraList);
  let paraIdx = paraArr.indexOf(para);
  if(paraIdx === -1) return undefined;
  if(paraIdx > 0) {
    return paraArr[paraIdx - 1];
  } else {
    return null;
  }
}

// offspring
function findFirstLeaf(node) {
  if(!node || !(node instanceof Node)) {
    console.error(`find first leaf: node-${node} is not a node, return null`);
    return null;
  }

  if(node.nodeType !== Node.ELEMENT_NODE) return node;
  else {
    let res = node;
    while(res.firstChild) res = res.firstChild;
    return res;
  }
}

function findLastLeaf(node) {
  if(!node || !(node instanceof Node)) {
    console.error(`find last leaf: node-${node} is not a node, return null`);
    return null;
  }

  if(node.nodeType !== Node.ELEMENT_NODE) return node;
  else {
    let res = node;
    while(res.lastChild) res = res.lastChild;
    return res;
  }
}

// block
function findBlock(node, root) {
  if(!node) return null;
  let res = node;
  while( res && res !== root &&
    !(res.nodeType === Node.ELEMENT_NODE && blockTagNames.includes(res.tagName))) {
    res = res.parentNode;
  }
  return res;
}

function findCode(range) {
  if(range instanceof Range) {
    let sc = range.startContainer, ec = range.endContainer;

    let sb = findBlock(sc), eb = findBlock(ec);

    if(sb === eb && sb.tagName === 'CODE') return sb;
    return null;
  } else if(range instanceof Node) {
    let block = findBlock(range);

    if(block.tagName === "CODE") return block;
    else return null;
  } else {
    console.warn(`find code: range-${range} is not a range or a node, return null`);
    return null;
  }
}

// text
function findPreviousContent(node) {
  let para = findPara(node);
  let contetNodes = getContentNodesFromNodeOrFrag(para);
  let idx = contetNodes.indexOf(node);
  if(idx > 0) return contetNodes[idx - 1];
  else return contetNodes[0];
}

// element
function findEle(node) {
  if(!(node instanceof Node)) {
    return null;
  }
  while(node && node.nodeType !== Node.ELEMENT_NODE) {
    node = node.parentNode;
  }
  return node;
}

function findPath(node, root) {
  let path = [node];
  while(node.parentNode && node.parentNode !== root) {
    path.push(node.parentNode);
    node = node.parentNode;
  }
  // console.log(node, root, path);
  return path;
}

function findLCA(node1, node2, root) {
  let path1 = findPath(node1, root);
  let path2 = findPath(node2, root);
  let flag = false, res = null;
  path1.forEach(node => {
    if(!flag && path2.includes(node)) {
    	flag = true;
    	res = node;
    }
  })
  return res;
}

export {
  findPara,
  findNextPara,
  findPreviousPara,

  findFirstLeaf,
  findLastLeaf,
  
  findBlock,
  findCode,

  findPreviousContent,

  findEle,

  findPath,
  findLCA,
}